昨天那份 .md 之所以是唯一真相,是因為三種產出都從它長出來。今天這份 CLAUDE.md 不一樣,但它長出來的不是檔案,而是行為 —— 而且它只有一個讀者:那個讀者不是人。
怎麼寫 CLAUDE.md,網路上已經很多人寫過,官方文件也寫得很細,我先前也在 Medium 拆過 Apple 那份 91 行的 CLAUDE.md(見參考資料)。所以今天不教怎麼寫。今天講兩個內建指令:/doctor 讀你的檔案,告訴你哪些該砍;/insights 讀你的 session,告訴你哪些該加。 兩個我都實跑了,數字在下面。
在那之前,需要一分鐘的地圖 —— 不然看不懂它們在砍什麼、加什麼。
第一,裡面每一句不是描述就是規定。 描述句像 Cloudflare 那份裡的 pnpm build — build the workspace(指令加一句它做什麼):AI 打開 package.json 就知道,你寫不寫都一樣。規定句像「不准把不可信輸入拼進 shell 指令」:沒有任何檔案能告訴它,因為那是一個決定。
第二,規定不保證被遵守。 官方文件寫得很直白:CLAUDE.md 對 Claude 是 context,不是強制設定。要「不管 AI 怎麼想都得擋住」,那是 lint、CI、hook 的事 —— 我把這種東西叫裁判。有裁判的規定是「擋」,沒裁判的是「請」。
第三,它不是一個檔案。 官方攤開來看,是四個層級加兩套系統,而且每一層進 context 的時機不同:
| 層級 | 位置 | 誰看得到 |
|---|---|---|
| 組織政策 | /Library/Application Support/ClaudeCode/CLAUDE.md(macOS) |
這台機器上每個人,個人設定蓋不掉 |
| 使用者 | ~/.claude/CLAUDE.md |
只有你,所有專案 |
| 專案 | ./CLAUDE.md 或 ./.claude/CLAUDE.md |
團隊,跟著版控走 |
| 本機 | ./CLAUDE.local.md |
只有你,這個專案(要進 .gitignore) |
由上而下依序載入,離你啟動位置越近的,它越晚讀到。想知道實際載入了哪些:/context,看 Memory files 那一欄。
但更容易搞混的是:同一個檔案裡的規則,其實有四個家:
| 這條東西 | 該放哪 | 什麼時候進 context |
|---|---|---|
| 每次都要遵守的規定 | CLAUDE.md |
每次 |
| 只在碰某類檔案時才適用的 | .claude/rules/ + paths: |
只有讀到符合的檔案時 |
| 給人看的理由、日期、踩過的坑 | CLAUDE.md 裡的 <!-- --> |
永遠不進(而且不花 token) |
| 它自己從你的糾正裡學到的 | auto memory(它自己寫) | 每次(索引前 200 行) |
中間兩格(paths: 和註解)跟直覺不一樣,我各驗了一次,證據留在這裡。
先驗註解。 官方文件說 block-level HTML 註解會在注入之前被剝掉。我分兩步驗。
第一步,它看不看得見。 同一個標記字串,只換位置:
| 標記放哪 | 問它「CLAUDE.md 裡有沒有這個字串」 |
|---|---|
<!-- ... --> 裡面 |
「沒有。」 |
| 註解外面,正常內文 | 「有。」 |
第二個是負對照。沒有這個對照,那句「沒有」可能只是它懶得看。
第二步,它花不花錢。 兩份 CLAUDE.md,規則內容一模一樣,差別只在其中一份帶了 600 行註解:
| 檔案大小 | 總輸入 token |
|---|---|
| 56,916 bytes | 20,769 |
| 16 bytes | 20,769 |
一個 token 都不差。
要分清楚的是:這不是模型「看得懂註解,所以主動略過」—— 任何模型直接收到含註解的文字,照樣會算進 token。這是 Claude Code 在載入時就把它剝掉了,根本沒送出去。官方文件原文:
Block-level HTML comments in CLAUDE.md files are stripped before the content is injected into Claude's context. […] Comments inside code blocks are preserved. When you open a CLAUDE.md file directly with the Read tool, comments remain visible.
所以有三個邊界:只有自成一段的 block-level 註解會被剝掉;程式碼區塊裡的註解保留;它用 Read 自己去讀那個檔案時,註解也看得到。
⚠️ 算 token 的時候有個坑:
cache_creation跟cache_read的分配每次都不一樣,只看其中一個會以為有差。要看input_tokens + cache_creation + cache_read的總和才穩定。
再驗 paths:。 .claude/rules/ 底下的檔案可以帶一段 YAML frontmatter:
---
paths:
- "src/api/**/*.ts"
---
# API 規則
- 所有 endpoint 都要輸入驗證
官方說它「只在 Claude 讀到符合的檔案時才載入」。我放了兩個暗號進去驗 —— 一條帶 paths,一條不帶:
| 探針 | 做了什麼 | 帶 paths 那條 |
不帶的那條 |
|---|---|---|---|
| 1 | 不讀任何檔 | 沒有 | 有 |
| 2 | 先讀 src/a.ts(符合) |
有 | — |
| 3 | 先讀 docs/note.md(不符合) |
沒有 | — |
探針 2 還自己交代了原因:「讀取 src/a.ts 後,路徑範圍規則被載入」。
這一條解掉了一個矛盾。 官方建議 CLAUDE.md 200 行以內,理由是太長會吃 context 而且降低遵循率;但真實專案的規則只會越來越多。答案不是一味寫短,而是別讓它全部一直載入 —— 而這正是 /doctor 等一下要做的事。
順帶一提,CLAUDE.md 跟別的 agent 讀的 AGENTS.md 可以共用一份:@AGENTS.md 一行 import,或者乾脆 symlink。這兩種都有大廠在用 —— Cloudflare workers-sdk 的 CLAUDE.md 全文 132 bytes,內容是 See @AGENTS.md;OpenAI openai-agents-python 的 CLAUDE.md 是指向 AGENTS.md 的 symlink。
要看 /doctor 砍得準不準,得先有一份人手分類過的。我挑 Cloudflare workers-sdk(wrangler、miniflare 的家;commit 71b6f10,2026-09-15)的 AGENTS.md,153 行,我逐句分類:
| 項數 | 內容 | |
|---|---|---|
| 描述句 | 30 | 8 條指令、13 列目錄地圖、9 句「工具鏈長這樣」 |
| 規定句 | 36 | 有裁判 13、半個裁判 2、找不到 21 |
有裁判的 13 條全是能寫成明確判斷式的 —— 不准 any(no-explicit-any: error)、關 lint 要寫理由(自寫規則)、改依賴要更新 lockfile(CI --frozen-lockfile)。找不到的 21 條全是判斷型 —— 「註解要講 why」「先讀該套件的 AGENTS.md」「用 SDK 不要自己打 REST」。
它的第一段自己就寫著「copied versions, rule lists, and counts become stale」,而第 140 行就應驗了這句話:寫的是 .github/PULL_REQUEST_TEMPLATE.md,tree 裡實際是全小寫的 pull_request_template.md。GitHub 兩種都認,人沒感覺;一個 agent 在 Linux 上照這行 Read,會拿到 file not found。連寫得這麼好的一份,也會過期。 這就是為什麼要有工具定期替你看。
/doctor:讓它替你砍官方定義它是一次「設定健檢」:查安裝有沒有重複或殘留、PATH、設定檔能不能解析;找沒在用的 skill、MCP server、plugin 各占多少 context;標出慢的 hook;查版本;然後是跟 CLAUDE.md 直接相關的三件事:**把本機的跟 checked-in 的去重;砍掉 checked-in 那份裡「Claude 從 codebase 就推得出來」的內容;把剩下每次都載入的指引搬進 skill 與巢狀 CLAUDE.md。**它先列發現、確認後才動手(修剪功能要 2.1.206 以上;別名 /checkup)。
我把 CLAUDE.md、AGENTS.md、package.json 放進一個空 repo,claude -p "/doctor"(2.1.271,343 秒、18 回合、$1.74)。它對 AGENTS.md 提了三刀,一刀都沒動,列完等我回 1、2、3:
| 它提議 | 內容 | 對上我手工分的哪一類 |
|---|---|---|
| 砍 | 4 條指令、目錄地圖 10 列、工具鏈那段,約 27 行 | 描述句 —— 幾乎重疊,它留了 3 列帶 gotcha 的 |
| 砍 | 「不准 any」「type-only import」「註解要講 why」 |
有裁判的規定 —— 理由:那節自己說 pnpm check 是權威,這些跟 lint 重複。並註明「這個 checkout 沒有 lint 設定,我沒法核對,要留就說」 |
| 搬 | Cross-Tool 整節 → skill;Testing Conventions → .claude/rules/testing.md 帶 paths: ["**/*.test.*", …];PR 那節 → skill;「不要直接 commit main」留根檔 |
沒裁判的判斷型規定 —— 它判斷每次都要在 context 裡的只剩幾條 |
它估:每個 session 從 2.3k 降到 1.2k tokens。
三刀剛好對上前面三類,只有第二刀是我原本沒想到的:有裁判的規定它也砍。 想一下確實合理 —— 裁判在,規則寫在檔案裡只是重複 lint 會說的話;裁判不在,寫了也只是「請」。規則檔真正該留的,是「沒裁判、但每次都要遵守」的那幾條 —— 而那幾條最好還是按需載入。
同一次跑它也掃了我的環境(它掃的是整台機器最近 50 個 session、14 個專案資料夾),跟 CLAUDE.md 無關但值得看:
skillOverrides 一鍵關掉,可逆AGENTS.md 2.3k、我的 ~/.claude/CLAUDE.md 0.4k、MCP 0 因為全部延遲載入);清完估 2.5k
三件要知道的:
/context 才是活的數字。/insights 的方向。/insights:讓它替你加/doctor 讀檔案,/insights 讀你過去的 session。它分析這台機器上最近的對話(一次最多分析 200 個沒看過的 session,太短的會跳過),產一份 HTML 報告到 ~/.claude/usage-data/report.html,八節:你在做什麼、你怎麼用、做得不錯的、哪裡出錯、還沒用的功能、新用法、更遠的、給團隊的回饋。跟 CLAUDE.md 直接相關的是「哪裡出錯」,和「還沒用的功能」底下那節 「Suggested CLAUDE.md Additions」:每條規則附上它從哪幾次摩擦推出來的理由,一鍵複製。
我跑了一次:69 個 session(178 個裡跳過太短的)、95 秒、$3.03。摩擦的分布長這樣:
| 摩擦類型 | 次數 |
|---|---|
| 寫出 bug | 36 |
| 方向錯 | 17 |
| 環境問題 | 8 |
| 誤解需求 | 4 |
| 我拒絕了它的動作 | 4 |
然後給了 6 條建議加進 CLAUDE.md 的規則。其中一條是這樣的(其他五條含我的帳號與工作習慣,不放):
Verification Discipline —— 報數字之前,用第二種獨立的方法再驗一次;一次性腳本算出來的數字不要直接講。理由:好幾次 session 的計數事後才更正,含
XPCKeys119 vs 82 那次。
那正是 Day 3 對賬表裡,我數錯的那一格(XPCKeys 的 case 數)。我沒告訴它,它從 session 紀錄裡自己撈出來的。
不要「Copy All」。 它的建議是模型讀你的 session 推出來的,同一個錯,它看到一次就可能想把它變成規則。六條我逐條問同一個問題:這條配得上裁判嗎?
CLAUDE.md,它會是「請」。pre-commit hook 十行就擋住了。這種不要寫成句子,寫成 hook。nohup 背景跑」—— 配得上一半:可以留下原則,但真正的解法是換一種方式啟動 process。也就是說,/insights 給你的只是候選,不是規則。它幫你做的是那件最難的事 —— 從 69 個 session 裡找出你一直在重複糾正的東西;配不配裁判、放哪一層、要不要按需載入,還是回到前面那張四個家的表。
代價與隱私: 一次大約 $3,用的是你的帳號額度;報告包含這台機器上所有專案的 session 摘要,別隨手分享。
CLAUDE.md 怎麼寫,我今天其實只講三件事:每句不是描述就是規定;規定要配裁判,配不上的是「請」;它不是一個檔案,是四層兩系統。然後把修檔案這件事交出去:
/doctor 從檔案往下砍:砍描述句、砍有裁判的規定(裁判在就不用重複)、把判斷型規定搬去按需載入。在真實 repo 裡跑,它才看得到裁判。/insights 從 session 往上加:從你一直在重複糾正的東西裡長出候選規則。逐條問配不配得上裁判,配得上的寫成 hook,不寫成句子。砍與加之間留下的,才是 CLAUDE.md 真正該有的樣子:規定句、沒裁判、每次都要。
這一篇留下的心法:
CLAUDE.md裡的每一句,不是描述就是規定。描述交給檔案系統;規定要有 lint、CI 或 hook 在背後擋 —— 沒有裁判的,AI 可能會跳過,而你不會知道。砍的事讓/doctor做,加的事讓/insights做;配不配得上裁判,自己判斷。
Claude Code 官方文件 — /doctor 與 /insights:code.claude.com/docs/en/commands、costs#analyze-your-usage-patterns(2026-09-16 查) —— 兩個實跑:/doctor 343 秒/18 回合/$1.74(空 repo 只放 Cloudflare 三個檔),/insights 69 個 session/95 秒/$3.03;Claude Code 2.1.271。/insights 報告含個人 session 內容,未公開
Claude Code 官方文件 — Memory / CLAUDE.md:code.claude.com/docs/en/memory(2026-09-16 核對)—— 四個層級與載入順序、.claude/rules/ 的 paths:、auto memory、AGENTS.md 互通、200 行建議;HTML 註解剝除的原文在 How CLAUDE.md files load 那節
官方 Hooks 文件(「要不管 Claude 怎麼想都得擋」時去這裡):code.claude.com/docs/en/hooks-guide
Cloudflare workers-sdk,commit 71b6f102f258e14e2b1dc23e9643cc74685d35cb(2026-09-15):github.com/cloudflare/workers-sdk—— CLAUDE.md(132 bytes)、AGENTS.md(153 行);裁判核對用到 .oxlintrc.jsonc、packages/lint-config-shared/rules/、tools/deployments/validate-*.ts、CI 的 install action
OpenAI openai-agents-python:CLAUDE.md 是指向 AGENTS.md 的 symlink(git tree mode 120000;2026-09-16 查):github.com/openai/openai-agents-python
兩個實測的重現指令(Claude Code 2.1.271,2026-09-16 重跑;9/11 在 2.1.267 第一次跑,結論相同):
mkdir -p t && cd t
printf '# t\n- 規則一\n\n<!--\n標記 ZEBRA_NOTE_7741\n-->\n' > CLAUDE.md
claude -p --output-format json "CLAUDE.md 裡有沒有 ZEBRA_NOTE_7741?不要用工具讀檔。"
看 usage 欄位,比較 input_tokens + cache_creation + cache_read 的總和。
延伸閱讀:Apple 官方的 CLAUDE.md:91 行,零句廢話——那你的呢?—— 「怎麼寫」在那篇,這篇是「怎麼讓工具替你改」
awesome-claude-md —— Cloudflare 那份是從這裡的 Top Picks 挑的